package org.zyzboole.base.encrypt.rewrite;


import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import net.sf.jsqlparser.JSQLParserException;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.reflection.MetaObject;
import org.zyzboole.base.encrypt.core.CCJSQLStatementContext;
import org.zyzboole.base.encrypt.core.OriginalFiledDTO;
import org.zyzboole.base.encrypt.crypto.encryption.EncryptDispose;
import org.zyzboole.base.encrypt.crypto.encryption.EncryptFactory;
import org.zyzboole.base.encrypt.crypto.enums.EncryptEnum;
import org.zyzboole.base.encrypt.properties.EnCryptProperties;
import org.zyzboole.base.encrypt.rewrite.condition.EncryptCondition;
import org.zyzboole.base.encrypt.rule.EncryptRule;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @author zyz
 */
public class EncryptPreParameterRewriter {

    private final EnCryptProperties properties;

    private final EncryptRule encryptRule;

    private final EncryptDispose dispose;

    private final String CENT = "%";

    public EncryptDispose getDispose() {
        return dispose;
    }

    public EncryptPreParameterRewriter(EnCryptProperties properties) {
        this.properties = properties;
        dispose = EncryptFactory.getDispose(EncryptEnum.getStatusEnum(properties.getKey()));
        encryptRule = new EncryptRule(properties);
    }

    public List<OriginalFiledDTO> rewrite(String sql, List<ParameterMapping> parameterMappings, MetaObject metaObjects) throws JSQLParserException {

        CCJSQLStatementContext context = new CCJSQLStatementContext(sql, encryptRule);

        List<EncryptCondition> encryptConditions = context.createEncryptConditions();
        List<OriginalFiledDTO> originals = new ArrayList<>(encryptConditions.size());
        if (encryptConditions.isEmpty()) {
            return originals;
        }
        // 存储加密过数据     主要用于限制加密过字段再次加密
        List<String> existList = new ArrayList<>(encryptConditions.size());
        for (EncryptCondition each : encryptConditions) {
            OriginalFiledDTO original = encryptParameters(each, parameterMappings, metaObjects, existList);
            if (!Objects.isNull(original)) {
                originals.add(original);
            }
        }
        return originals;
    }

    private OriginalFiledDTO encryptParameters(EncryptCondition encryptCondition, List<ParameterMapping> parameterMappings, MetaObject metaObject, List<String> existList) {
        int index = encryptCondition.getIndex();

        if (parameterMappings != null && metaObject != null) {
            ParameterMapping parameterMapping = parameterMappings.get(index - 1);
            String property = parameterMapping.getProperty();
            if (existList.contains(property)) {
                return null;
            }
            Object value = metaObject.getValue(property);
            if (value instanceof String) {
                String originalValues = (String) metaObject.getValue(property);
                boolean starts = originalValues.startsWith(CENT);
                boolean ends = originalValues.endsWith(CENT);
                String originalValues2 = starts || ends ? originalValues.replace(CENT, StringUtils.EMPTY) : originalValues;
                Object encryptedValues = encryptRule.getEncryptedValues(encryptCondition.getTableName(), encryptCondition.getColumnName(), originalValues2);
                if (starts) {
                    encryptedValues = (CENT + encryptedValues.toString());
                }
                if (ends) {
                    encryptedValues = (encryptedValues + CENT);
                }
                metaObject.setValue(property, encryptedValues);
                existList.add(property);
                return new OriginalFiledDTO(property, originalValues);
            }
        }
        return null;
    }
}
